package com.beiding.workordersystem.orders.generalorder.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.beiding.workordersystem.common.beans.MsgSender;
import com.beiding.workordersystem.common.dao.MPage;
import com.beiding.workordersystem.common.observer.UserObserver;
import com.beiding.workordersystem.common.service.BaseService;
import com.beiding.workordersystem.common.standard.E;
import com.beiding.workordersystem.common.utils.*;
import com.beiding.workordersystem.customer.dao.CustomerDao;
import com.beiding.workordersystem.customer.entity.CustomerEntity;
import com.beiding.workordersystem.orders.common.entity.Commission;
import com.beiding.workordersystem.orders.common.service.CommissionService;
import com.beiding.workordersystem.orders.generalorder.dao.GeneralOrderDao;
import com.beiding.workordersystem.orders.generalorder.dao.OrderProcessDao;
import com.beiding.workordersystem.orders.generalorder.entity.GeneralOrder;
import com.beiding.workordersystem.orders.generalorder.entity.OrderProcess;
import com.beiding.workordersystem.orders.generalorder.model.ProcessModel;
import com.beiding.workordersystem.orders.generalorder.model.ProcessMoon;
import com.beiding.workordersystem.orders.generalorder.model.ProcessReward;
import com.beiding.workordersystem.orders.generalorder.service.OrderProcessService;
import com.beiding.workordersystem.system.service.UserService;
import com.beiding.workordersystem.system.service.VariableService;
import com.gitee.beiding.timer.MyTimer;
import com.gitee.beiding.timer.annotation.AspectHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class OrderProcessServiceImpl extends BaseService implements OrderProcessService, UserObserver {

    @Autowired
    private GeneralOrderDao generalOrderDao;

    @Autowired
    private OrderProcessDao orderProcessDao;

    @Autowired
    private UserService userService;

    @Autowired
    private MyTimer myTimer;

    @Override
    @Transactional
    public void designate(String orderId, Integer type, List<OrderProcess> orderProcesses) {

        //判断工单状态
        Integer status = generalOrderDao.getStatus(orderId);
        E.notNull(status, "工单不存在");
        E.isTrue(status == 3 || status == 4, "工单状态不符");

        //对工序进行排序
        orderProcesses.sort(Comparator.comparingInt(OrderProcess::getSort));

        //重新格式化工序
        for (int i = 0; i < orderProcesses.size(); i++) {
            OrderProcess process = orderProcesses.get(i);
            process.setSort(i);
            E.isFalse(process.getCommissionRate() != null && process.getCommissionFee() != null, "不能同时指定比例和金额");
            if (process.getCommissionRate() != null) {
                E.isTrue(process.getCommissionRate() <= 100, "无效的百分比");
            }
        }

        //更新工序状态
        generalOrderDao.designate(orderId, type);

        //删除当前所有工序重新插入
        orderProcessDao.delete(new QueryWrapper<OrderProcess>().lambda().eq(OrderProcess::getOrderId, orderId));

        //反序插入,令每个工序都知道自己的下一道工序(除了最后一道工序)
        String nextId = null;

        if (type == 1 && orderProcesses.get(0).getPrincipalId() == null) {
            E.error("抢单类工单,第一道工序必须指定负责人");
        }

        for (int i = orderProcesses.size() - 1; i >= 0; i--) {
            OrderProcess orderProcess = orderProcesses.get(i);

            orderProcess.setType(type);
            if (notEmpty(orderProcess.getPrincipalId())) {
                //工序负责人必须存在
                E.isTrue(userService.existById(orderProcess.getPrincipalId()), "工序负责人不存在,无法指派");
            }
            fillId(orderProcess);
            orderProcess.setNextProcessId(nextId);
            nextId = orderProcess.getId();
            orderProcess.setStatusCode(0);
            orderProcessDao.insert(orderProcess);
        }

    }

    @Override
    public IPage getMyProcesses(MPage mPage) {

        //获取我的工序,只有激活的工序才可见
        String userId = MySecurityUtils.currentCredentials().getUserId();
        return orderProcessDao.selectPage(mPage.toIPage(), new QueryWrapper<OrderProcess>().lambda().eq(OrderProcess::getPrincipalId, userId).eq(OrderProcess::getActive, true));
    }


    @Override
    @Transactional
    public void reportException(String processId, String exception) {
        OrderProcess orderProcess = orderProcessDao.selectById(processId);
        E.isTrue(notEmpty(orderProcess), "工序不存在");

        //工序负责人必须和当前用户相同
        E.isTrue(MySecurityUtils.currentCredentials().getUserId().equals(orderProcess.getPrincipalId()), "你不是该工序负责人,无权操作");

        //只有对待受理和进行中的工序提报异常
        E.isTrue(orderProcess.getStatusCode() == 0 || orderProcess.getStatusCode() == 1, "订单不存在或状态不符");

        orderProcessDao.exception(processId, exception);

        //更新工单状态为异常
        generalOrderDao.updateStatus(orderProcess.getOrderId(), 5);
    }

    @Override
    @Transactional
    public void accept(String[] ids) {
        for (String id : ids) {
            accept(id);
        }
    }

    @Override
    @Transactional
    public void publish(String[] ids) {
        for (String id : ids) {
            publish(id);
        }
    }

    @Override
    @Transactional
    public void changePrincipal(String processId, String principalId) {

        principalId = principalId.trim();
        OrderProcess orderProcess = orderProcessDao.selectById(processId);
        E.notNull(orderProcess, "工序不存在");
        E.isTrue(orderProcess.getType() == 1, "该工序不可切换负责人");
        if (orderProcess.getStatusCode() == 2) {
            E.noEmpty(principalId, "必须给定负责人");
            E.isTrue(userService.existById(principalId), "新指定的负责人不存在");
            orderProcessDao.changePrincipalAndRestart(processId, principalId);
        } else {
            E.isTrue(!orderProcess.getActive() && !orderProcess.getComplete(), "当前状态不可变更负责人");
            if (principalId.equals("")) {
                orderProcess.setPrincipalId(null);
            } else {
                E.isTrue(userService.existById(principalId), "新指定的负责人不存在");
                orderProcess.setPrincipalId(principalId);
            }
            orderProcessDao.updateById(orderProcess);
        }

        //将工单状态恢复
        generalOrderDao.updateStatus(orderProcess.getOrderId(), 6);
    }

    @Autowired
    private CommissionService commissionService;

    @Override
    @Transactional
    public void finish(String id, boolean sendMsg) {

        //TODO 短信以及资料处理方式
        OrderProcess orderProcess = orderProcessDao.selectById(id);
        E.notNull(orderProcess, "工序不存在");
        E.isTrue(orderProcess.getStatusCode() == 1, "状态不合法");

        //在完成之后依然处于激活状态,但是

        //标记为可更改状态
        orderProcess.setComplete(true);
        orderProcess.setStatusCode(3);
        orderProcessDao.updateById(orderProcess);
        if (orderProcess.getNextProcessId() == null) {
            GeneralOrder generalOrder = generalOrderDao.selectById(orderProcess.getOrderId());
            generalOrder.setComplete(true);
            generalOrder.setStatusCode(7);
            generalOrderDao.updateById(generalOrder);
        } else {
            //更新状态为审核流转
            generalOrderDao.updateStatus(orderProcess.getOrderId(), 9);
            orderProcessDao.setCurrent(orderProcess.getNextProcessId());
        }


        Commission commission = new Commission();

        commission.setBigType(orderProcess.getBigType());
        commission.setWork(orderProcess.getWork());
        commission.setWorkDetails(orderProcess.getWorkDetails());
        commission.setBusinessType(orderProcess.getBusinessType());
        commission.setUserId(orderProcess.getPrincipalId());
        commission.setSatrapId(orderProcess.getSatrapId());
        commission.setValue(orderProcess.getCommissionFee());
        commission.setSatrapValue(orderProcess.getSatrapCommissionFee());
        commission.setCompleteTime(new Date());
        //完结之后添加提成
        commissionService.save(commission);

        //如果需要发送短信进行提醒
        if (sendMsg) {
            String orderId = orderProcess.getOrderId();
            GeneralOrder generalOrder = generalOrderDao.selectById(orderId);
            CustomerEntity select = customerDao.selectById(generalOrder.getCustomerId());
            if (select.getPhone() != null) {

                Map<String, String> params = new HashMap<>();
                params.put("name", select.getName());

                //生成20位随机码并绑定工序id
                String code = RandomUtils.createRandomString(19);
                variableService.set(code, orderProcess.getId());

                //最后一位标记工单类型,如果是0表示普通工单,1则表示记账报税
                params.put("code", code + "0");

                sender.send(select.getPhone(), "SMS_184206018", params);
                //两天有效
                ApplicationUtils.get(this).removeDetailsCode(code, 48 * 60 * 60 * 1000);
            }
        }

    }

    @AspectHandler
    public void removeDetailsCode(String key, long time) {
        variableService.set(key, null);
    }

    @Autowired
    private VariableService variableService;

    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private MsgSender sender;

    @Override
    @Transactional
    public void activeNextProcess(String id) {

        //获取当前工序
        OrderProcess orderProcess = orderProcessDao.selectOne(QueryUtils.getQuery(OrderProcess.class).eq(OrderProcess::getOrderId, id).eq(OrderProcess::getActive, true));

        E.notNull(orderProcess, "当前工单无可流转工序");

        //将当前工序设置变更为未激活状态
        orderProcess.setActive(false);
        orderProcessDao.updateById(orderProcess);

        E.notNull(orderProcess.getNextProcessId(), "下一道工序不存在,无法激活");

        OrderProcess select = orderProcessDao.selectById(orderProcess.getNextProcessId());
        E.notNull(select, "下一道工序不存在,无法激活");

        E.isTrue(select.getType() == 1 && select.getPrincipalId() != null && !select.getPrincipalId().trim().equals(""), "下一道工序尚未制定负责人,无法流转");

        //标记为激活状态
        select.setActive(true);

        //更新
        orderProcessDao.updateById(select);

        generalOrderDao.updateStatus(id, 6);
        //超时未受理
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(new Date());
        gc.add(Calendar.DAY_OF_YEAR, select.getMaxTime());

        myTimer.schedule("generalProcessOverdue", select.getId(), gc.getTime());


    }


    private void publish(String id) {
        GeneralOrder generalOrder = generalOrderDao.getStatusType(id);
        E.isTrue(generalOrder != null && generalOrder.getStatusCode() == 4, "工单不存在或状态不满足预期");

        if (generalOrder.getType() == 1) {

            OrderProcess orderProcess = orderProcessDao.selectOne(QueryUtils.getQuery(OrderProcess.class).eq(OrderProcess::getOrderId, id).eq(OrderProcess::getSort, 0));

            orderProcess.setActive(true);
            orderProcess.setStatusCode(0);
            orderProcessDao.updateById(orderProcess);

            //超时未受理
            GregorianCalendar gc = new GregorianCalendar();

            //从当前时间推
            gc.setTime(new Date());
            gc.add(Calendar.DAY_OF_YEAR, orderProcess.getMaxTime());
            ApplicationUtils.get(this).generalProcessOverdue(orderProcess.getId(), gc.getTime());
            generalOrderDao.updateStatus(id, 6);
        } else {
            //TODO  抢单大厅,当类型为0,状态为6,且工单负责人为null则会出现在抢单大厅中
            generalOrderDao.updateStatus(id, 6);
        }


    }

    private void accept(String id) {
        OrderProcess statusAndPrincipalId = orderProcessDao.getStatusAndPrincipalId(id);
        E.isTrue(statusAndPrincipalId != null && statusAndPrincipalId.getStatusCode() == 0 && MySecurityUtils.currentCredentials().getUserId().equals(statusAndPrincipalId.getPrincipalId()), "工序不存在或状态异常或工序负责人不是你");
        orderProcessDao.updateStatus(id, 1);
    }

    @Override
    public void beforeDelete(String userId, String accountId, String name) {
        E.isFalse(orderProcessDao.exist(new QueryWrapper<OrderProcess>().lambda().eq(OrderProcess::getPrincipalId, userId)), name, "存在未完成工序,无法删除");
    }


    @Override
    public void substitution(String from, String to) {
        orderProcessDao.substitution(from, to);
    }


    @AspectHandler
    public void generalProcessOverdue(String id, Date time) {

        OrderProcess select = orderProcessDao.selectById(id);
        if (select != null && select.getStatusCode() == 0) {
            select.setStatusCode(4);
            orderProcessDao.updateById(select);
        }
    }


    //年月
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM");

    private SimpleDateFormat dateFormatShow = new SimpleDateFormat("yyyy年MM月");


    @Override
    public ProcessModel statistics(Date start, Date to) {
        //必须给出一个有效的区间
        if (start != null && to != null) {
            E.isTrue(to.getTime() >= start.getTime(), "无效时间区间");
        } else if (start == null && to == null) {
            to = new Date();
            start = DateCalculator.addYear(to, -1);
        } else {
            if (start == null) {
                start = DateCalculator.addYear(to, -1);
            } else {
                to = DateCalculator.addYear(start, 1);
            }
        }

        Map<String, Map<String, List<OrderProcess>>> map = new HashMap<>();

        List<String> head = new ArrayList<>();

        //从开始时间
        Date t = start;
        while (t.getTime() <= to.getTime()) {

            //格式化时间
            String format = dateFormat.format(t);

            //首先找到工单
            List<GeneralOrder> orders = generalOrderDao.selectList(QueryUtils.getQuery(GeneralOrder.class).likeRight(GeneralOrder::getCreateTime, format));

            String showDate = dateFormatShow.format(t);
            head.add(showDate);

            //根据工单找到相应的工序
            for (GeneralOrder order : orders) {

                //寻找相应的工序,
                List<OrderProcess> orderProcesses = orderProcessDao.selectList(QueryUtils.getQuery(OrderProcess.class).eq(OrderProcess::getOrderId, order.getId()));

                for (OrderProcess process : orderProcesses) {
                    if (process.getPrincipalId() != null) {
                        Map<String, List<OrderProcess>> user = map.computeIfAbsent(process.getPrincipalId(), k -> new HashMap<>());
                        List<OrderProcess> commissionList = user.computeIfAbsent(showDate, k -> new ArrayList<>());
                        commissionList.add(process);
                    }
                }
            }

            //增加一个月份
            t = DateCalculator.addMoon(t, 1);
        }

        List<ProcessReward> rewards = new ArrayList<>();

        //整合所有的记录
        map.forEach((userId, commissions) -> {

            ProcessReward reward = new ProcessReward();
            reward.setName(userService.getById(userId).getName());
            Map<String, ProcessMoon> moonMap = new HashMap<>();
            commissions.forEach((moon, list) -> {
                ProcessMoon m = new ProcessMoon();

                List<OrderProcess> complete = new ArrayList<>();
                List<OrderProcess> notComplete = new ArrayList<>();
                List<OrderProcess> other = new ArrayList<>();

                m.setAll(list);
                m.setAllNum(list.size());

                for (OrderProcess process : list) {
                    if (process.getComplete()) {
                        complete.add(process);
                    } else if (process.getStatusCode() == 1||process.getStatusCode()==2) {
                        notComplete.add(process);
                    } else {
                        other.add(process);
                    }
                }
                m.setComplete(complete);
                m.setCompleteNum(complete.size());
                m.setNotComplete(notComplete);
                m.setNotCompleteNum(notComplete.size());
                m.setOther(other);
                m.setOtherNum(other.size());
                moonMap.put(moon, m);
            });

            reward.setMoons(moonMap);
            rewards.add(reward);
        });

        ProcessModel orderModel = new ProcessModel();

        orderModel.setRewards(rewards);
        orderModel.setHead(head);

        return orderModel;
    }


}
